//
//  CMRequest.swift
//  covermedia
//
//  Created by travel on 2016/11/7.
//  Copyright © 2016年 covermedia. All rights reserved.
//

import UIKit
import Alamofire


/// 网络请求
public class CMRequest {
    fileprivate var key: String = "12345678"
    fileprivate var account: String = ""
    
    fileprivate var token: String = ""
    fileprivate var client: String = "iOS"
    fileprivate var version: String = Bundle.main.appVersion
    fileprivate var deviceid: String = "deviceid"
    
    fileprivate var manager:SessionManager!
    
    
    public init(timeout:TimeInterval = 3) {
        self.deviceid = CMDeviceUtils.getDeviceId()
        self.version = Bundle.main.appVersion
        
        let configuration = URLSessionConfiguration.default
        configuration.timeoutIntervalForRequest = timeout
        
        let cer = ServerTrustPolicy.certificates()
        let serverTrustPolicies: [String: ServerTrustPolicy] = [
            CMURLConstant.Host: .pinCertificates(certificates: cer,validateCertificateChain:true,validateHost:false)
        ]
        
        let sessionDelegate = SessionDelegate()
        sessionDelegate.sessionDidReceiveChallenge = { session, challenge in
            var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
            var credential: URLCredential?
            if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
                disposition = URLSession.AuthChallengeDisposition.useCredential
                credential = URLCredential.init(trust: challenge.protectionSpace.serverTrust!)
            } else {
                if challenge.previousFailureCount > 0 {
                    disposition = .cancelAuthenticationChallenge
                } else {
                    credential = self.manager.session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace)
                    if credential != nil {
                        disposition = .useCredential
                    }
                }
            }
            return (disposition, credential)
        }
        
        manager = SessionManager.init(configuration: configuration, delegate: sessionDelegate, serverTrustPolicyManager: ServerTrustPolicyManager.init(policies: serverTrustPolicies))
    }
    
    fileprivate func sign(_ key:String, account:String, token:String, timestamp: Int64) -> String {
        return "\(key)\(account)\(token)\(timestamp)".md5.uppercased()
    }
    
    
    fileprivate func buildParams(_ parameters: [String: Any]? = nil, hasLogin: Bool = true) -> ([String: Any]) {
        let key  = self.key
        var account = self.account
        var token = self.token
        
        
        if let user = CMUserProfileStorageService.shared.retrieveUserProfile(LoginManager.shareManager.currentUserID) {
            if let t = user.token {
                token = t
            }
            account = String(user.user_id)
        }
        
        let client = self.client
        let deviceid = self.deviceid
        let timestamp = Int64(Date.init().timeIntervalSince1970 * 1000)
        
        let data = parameters?.toJsonString() ?? ""
        if hasLogin {
            let sign = self.sign(key, account: account, token: token, timestamp: timestamp)
            
            let realParams = [
                "timestamp": String(timestamp),
                "token": token,
                "data": data,
                "client": client,
                "deviceid": deviceid,
                "sign": sign
            ]
            return realParams
        } else {
            let sign = self.sign(key, account: "", token: "", timestamp: timestamp)
            
            let realParams = [
                "timestamp": String(timestamp),
                "data": data,
                "client": client,
                "deviceid": deviceid,
                "sign": sign
            ]
            return realParams
        }
    }
    
}


// MARK: - 网络请求对外接口
extension CMRequest {
    public func request <T: CMResBase>(
        method: HTTPMethod,
        url: String,
        parameters: [String: Any]?,
        success: ((T?) -> Void)? = nil,
        failed:((CMBaseError) -> Void)? = nil,
        start:((Void) -> Void)? = nil,
        finish:((Void) -> Void)? = nil) {
        if let handler = start {
            handler()
        }
        var params:[String: Any]?
        if url == CMURLConstant.quickLogin.urlConstant ||
            url == CMURLConstant.register.urlConstant ||
            url == CMURLConstant.login.urlConstant ||
            url == CMURLConstant.forgetPassword.urlConstant {
            params = self.buildParams(parameters, hasLogin: false)
        } else {
            params = self.buildParams(parameters, hasLogin: true)
        }
        
        let request = manager.request("\(url)?vno=\(version)", method: method, parameters: params)
            .responseObject(completionHandler: { (response: DataResponse<T>) in
                var error = CMBaseError.requestError
                switch(response.result) {
                case let .success(result):
                    if result.isSucceed {
                        success?(result)
                        finish?()
                        return
                    }
                    
                    error.message = result.message
                    // 设置错误类型
                    if let code = CMBaseErrorCode(rawValue: result.status) {
                        error.code = code
                    }
                case let .failure(err):
                    if err._code == NSURLErrorTimedOut {
                        error = CMBaseError.timeOutError

                    } else if !CMNetwork.isReachable {
                         error = CMBaseError.noNetworkError
                    }
                }
                
                error.message = error.message ?? CMBaseError.messageRequestError
                // 预处理错误信息
                error.requestPreHandle()
                
                // 处理错误信息
                failed?(error)
                finish?()
            })
        #if DEBUG
            if kAppRequestLog {
                request.responseJSON { response in
                    debugPrint("http url: \(url)")
                    debugPrint("params: \(params)")
                    debugPrint(response)
                }
            }
        #endif
    }
    
    /// 上传图片
    ///
    /// - parameter url:     地址
    /// - parameter images:  图片列表
    /// - parameter success: 成功处理
    /// - parameter failed:  失败处理
    /// - parameter start:   开始处理
    /// - parameter finish:  结束操作
    func uploadImages <T: CMResBase> (
        url: String,
        images: [UIImage],
        success: ((T) -> Void)? = nil,
        failed:((CMBaseError) -> Void)? = nil,
        start:((Void) -> Void)? = nil,
        finish:((Void) -> Void)? = nil) {
        if let handler = start {
            handler()
        }
        
        start?()
        
        let parameters = buildParams(nil, hasLogin: false)
        let imageDatas = images.flatMap({ UIImageJPEGRepresentation($0, 0.5) })
        manager.upload(
            multipartFormData: { (multi:MultipartFormData) in
                for (index, img) in imageDatas.enumerated() {
                    multi.append(img, withName: "file", fileName: "file\(index).png", mimeType: "image/png")
                }
                for (key, v) in parameters {
                    multi.append("\(v)".data(using: String.Encoding.utf8)!, withName: key)
                }
        },
            to: url) { (result: SessionManager.MultipartFormDataEncodingResult) in
                switch result {
                case .success(let upload, _, _):
                    upload.responseObject(completionHandler: { (response: DataResponse<T>) in
                        var error = CMBaseError.requestError
                        switch(response.result) {
                        case let .success(result):
                            if result.isSucceed {
                                success?(result)
                                finish?()
                                return
                            }
                            
                            error.message = result.message
                            // 设置错误类型
                            if let code = CMBaseErrorCode(rawValue: result.status) {
                                error.code = code
                            }
                        case let .failure(err):
                            if err._code == NSURLErrorTimedOut {
                                error = CMBaseError.timeOutError
                                
                            } else if !CMNetwork.isReachable {
                                error = CMBaseError.noNetworkError
                            }
                        }
                        
                        error.message = error.message ?? NSLocalizedString("request error", comment: "request error")
                        failed?(error)
                        finish?()
                    })
                    #if DEBUG
                        if kAppRequestLog {
                            upload.responseJSON { response in
                                debugPrint("http url: \(url)")
                                debugPrint("params: \(parameters)")
                                debugPrint(response)
                            }
                        }
                    #endif
                case .failure(_):
                    let error = CMBaseError.requestError
                    error.message = error.message ?? NSLocalizedString("request error", comment: "request error")
                    error.code = .failed
                    failed?(error)
                    finish?()
                }
        }
    }
}

// MARK: - 网络请求方便使用的函数
public extension CMRequest {
    // * 外部调用 *
    public func post <T: CMResBase>(
        url: String,
        parameters: [String: Any]?,
        success: ((T?) -> Void)? = nil,
        failed:((CMBaseError) -> Void)? = nil,
        start:((Void) -> Void)? = nil,
        finish:((Void) -> Void)? = nil)
    {
        self.request(method: .post, url: url, parameters: parameters, success: success, failed: failed, start: start, finish: finish)
    }
    
    public func post <T: CMResBase>(
        url: CMURLConstant,
        parameters: [String: Any]?,
        success: ((T?) -> Void)? = nil,
        failed:((CMBaseError) -> Void)? = nil,
        start:((Void) -> Void)? = nil,
        finish:((Void) -> Void)? = nil)
    {
        self.request(method: .post, url: url.urlConstant, parameters: parameters, success: success, failed: failed, start: start, finish: finish)
    }
    
    public func get <T: CMResBase>(
        url: CMURLConstant,
        parameters: [String: Any]?,
        success: ((T?) -> Void)? = nil,
        failed:((CMBaseError) -> Void)? = nil,
        start:((Void) -> Void)? = nil,
        finish:((Void) -> Void)? = nil)
    {
        self.request(method: .get, url: url.urlConstant, parameters: parameters, success: success, failed: failed, start: start, finish: finish)
    }
    
    public func uploadImages <T: CMResBase> (
        url: CMURLConstant,
        images: [UIImage],
        success: ((T) -> Void)? = nil,
        failed:((CMBaseError) -> Void)? = nil,
        start:((Void) -> Void)? = nil,
        finish:((Void) -> Void)? = nil) {
        self.uploadImages(url: url.urlConstant, images: images, success: success, failed: failed, start: start, finish: finish)
    }
}



public enum CMNetRequestType: Int {
    case shared
    case long
}
private class CMNetRequestCenter {
    fileprivate var center:[CMNetRequestType:CMRequest] = [:]
    static let sharedCenter = CMNetRequestCenter()
    
}

public extension CMRequest {
    public static var shared:CMRequest {
        return service(type: .shared)
    }
    
    public static var long:CMRequest {
        return service(type: .long)
    }
    
    public class func service(type:CMNetRequestType) -> CMRequest {
        let center = CMNetRequestCenter.sharedCenter
        if let service = center.center[type] {
            return service
        } else {
            var timeout:TimeInterval = 3
            switch type {
            case .shared:
                timeout = 3
            case .long:
                timeout = 8
            }
            let service = CMRequest.init(timeout: timeout)
            center.center[type] = service
            return service
        }
    }
}



// MARK: - 错误信息处理
public extension CMBaseError {
    
    
    /// 错误信息前置处理
    public func requestPreHandle() {
        switch code {
        case .authorizeTimeout:
            // 将用户登录信息清空
            LoginManager.shareManager.loginState = LoginState.unlogin
            LoginManager.shareManager.currentUserID = -1
        default:
            break
        }
    }
}
